﻿using System;
using System.Diagnostics;
using System.IO;
using Seagull.Generic;
using Seagull.Model.Stat;

namespace Seagull.DAL.Stat
{
    [DebuggerDisplay("{ToString()}")]
    public class ChannelStatFileWriter
    {
        private readonly byte _channel;
        private static FileInfo _channelStatFile;
        private DateTime _fileStartTime;

        public ChannelStatFileWriter(byte channel)
        {
            _channel = channel;
            _channelStatFile = PathHelper.GetChannelStatFile(channel);
            PathHelper.EnsureDirectoryRecursively(_channelStatFile.Directory);
            _fileStartTime = GetFileStartTime(_channelStatFile);
        }

        public void Append(TimeStatistic timeStatistic)
        {
            using (var fileStream = FileHelper.Open(() => _channelStatFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)))
            {
                using(var writer = new BinaryWriter(fileStream))
                {
                    if(timeStatistic.Time >= _fileStartTime)
                    {
                        // 在现有文件起始时间之后
                        if(_fileStartTime == DateTime.MinValue)
                        {
                            TimestampAccessor.Write(writer, timeStatistic.Time);
                            _fileStartTime = timeStatistic.Time;
                        }

                        var offset = CalcOffsetFromBegin(timeStatistic.Time - _fileStartTime);
                        writer.BaseStream.Seek(offset, SeekOrigin.Begin);
                        WriteTimeStatistic(writer, timeStatistic);
                    }
                    else
                    {
                        // 需要移动通道统计文件内容
                        // 首先读取所有统计数据
                        var buffer = new MemoryStream((int)(fileStream.Length));
                        fileStream.Seek(sizeof(long), SeekOrigin.Begin);
                        fileStream.CopyTo(buffer);

                        // 移到文件头，写入新的起始时间
                        fileStream.Seek(0, SeekOrigin.Begin);
                        TimestampAccessor.Write(writer, timeStatistic.Time);

                        // 写入新的统计数据
                        WriteTimeStatistic(writer, timeStatistic);
                        writer.Flush();

                        // 截断文件，以确保后面全部为0
                        fileStream.SetLength(fileStream.Position);

                        // 计算原统计数据的偏移量并移动文件指针
                        var originalStatOffset = CalcOffsetFromBegin(_fileStartTime - timeStatistic.Time);
                        fileStream.Seek(originalStatOffset, SeekOrigin.Begin);

                        // 写入原有的统计数据
                        buffer.Seek(0, SeekOrigin.Begin);
                        buffer.CopyTo(fileStream);

                        _fileStartTime = timeStatistic.Time;
                    }
                    writer.Flush();
                }
            }
        }

        private static long CalcOffsetFromBegin(TimeSpan timeDiff)
        {
            return (long)(sizeof(long) + timeDiff.TotalHours * 2);
        }

        private static void WriteTimeStatistic(BinaryWriter writer, TimeStatistic timeStatistic)
        {
            writer.Write(timeStatistic.Statistic.Max);
            writer.Write(timeStatistic.Statistic.Avg);
        }

        public static DateTime GetFileStartTime(FileInfo channelStatFile, DateTime newStartTimeIfNotExist)
        {
            DateTime fileStartTime;
            channelStatFile.Refresh();
            if(!channelStatFile.Exists)
            {
                using(var writer = new BinaryWriter(channelStatFile.Create()))
                {
                    TimestampAccessor.Write(writer, newStartTimeIfNotExist);
                    fileStartTime = newStartTimeIfNotExist;
                }
            }
            else
            {
                fileStartTime = GetFileStartTime(channelStatFile);
            }
            return fileStartTime;
        }

        private static DateTime GetFileStartTime(FileInfo channelStatFile)
        {
            channelStatFile.Refresh();

            if(channelStatFile.Exists)
            {
                using(var reader = new BinaryReader(channelStatFile.OpenRead()))
                {
                    return TimestampAccessor.Read(reader);
                }
            }

            return DateTime.MinValue;
        }

        public override string ToString()
        {
            return string.Format("Channel: {0}", _channel);
        }
    }
}
